#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <chrono>
#include <thread>

#include "safe_timer.h"

class CallbackMock {
 public:
  MOCK_METHOD(void, CallbackMethod, ());
};

class SafeTimerTest : public testing::Test {
 protected:
  CallbackMock callback_mock;

  void SetUp() override {
    // Do nothing now
  }

  void TearDown() override {
    // Do nothing now
  }
};

TEST_F(SafeTimerTest, SingleShot) {
  SafeTimer timer("TestSingleShot");
  EXPECT_CALL(callback_mock, CallbackMethod()).Times(1);
  int time_ms = 100;  // Delay time in milliseconds
  bool ret = timer.SingleShot(time_ms, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for an additional 100ms to ensure execution
  std::this_thread::sleep_for(std::chrono::milliseconds(time_ms + 100));
}

TEST_F(SafeTimerTest, RepeatWithParamCallImmediately) {
  SafeTimer timer("TestRepeatWithParamCallImmediately");
  int repeat_count = 3;  // Number of times repeat should execute
  int time_ms = 200;     // Delay time in milliseconds

  EXPECT_CALL(callback_mock, CallbackMethod()).Times(repeat_count);
  // Execute once immediately
  auto ret = timer.Repeat(time_ms, true, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for an additional 100ms to ensure execution
  std::this_thread::sleep_for(std::chrono::milliseconds((repeat_count - 1) * time_ms + 100));

  // Cancel previous timer
  timer.Cancel();
  EXPECT_CALL(callback_mock, CallbackMethod()).Times(repeat_count);
  // Do not execute immediately
  ret = timer.Repeat(time_ms, false, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for an additional 100ms to ensure execution
  std::this_thread::sleep_for(std::chrono::milliseconds(repeat_count * time_ms + 100));
}

TEST_F(SafeTimerTest, RepeatWithoutParamCallImmediately) {
  SafeTimer timer("TestRepeatWithoutParamCallImmediately");
  int repeat_count = 3;  // Number of times repeat should execute
  int time_ms = 500;     // Delay time in milliseconds

  EXPECT_CALL(callback_mock, CallbackMethod()).Times(repeat_count);
  auto ret = timer.Repeat(time_ms, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for an additional 100ms to ensure execution
  std::this_thread::sleep_for(std::chrono::milliseconds(repeat_count * time_ms + 100));
}

TEST_F(SafeTimerTest, Cancel) {
  SafeTimer timer("Cancel");
  int repeat_count = 3;  // Number of times repeat should execute
  int time_ms = 500;  // Delay time in milliseconds

  EXPECT_CALL(callback_mock, CallbackMethod()).Times(repeat_count - 1);
  // Execute once immediately
  auto ret = timer.Repeat(time_ms, true, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for 100ms less to ensure cancel is called in time
  std::this_thread::sleep_for(std::chrono::milliseconds((repeat_count - 1) * time_ms - 100));
  timer.Cancel();
}

// Test if cancelling immediately after timer creation causes any issues
// Expected: Cancelling immediately after timer creation should directly return and perform no operation
TEST_F(SafeTimerTest, CancelBeforeSingleShot) {
  SafeTimer timer("TestCancelBeforeSingleShot");
  EXPECT_CALL(callback_mock, CallbackMethod()).Times(1);
  timer.Cancel();
  int time_ms = 100;  // Delay time in milliseconds
  auto ret = timer.SingleShot(time_ms, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for an additional 100ms to ensure execution
  std::this_thread::sleep_for(std::chrono::milliseconds(time_ms + 100));
}

// Test if cancelling immediately after creating a SingleShot timer causes any issues
// Expected: Properly cancel without issues
TEST_F(SafeTimerTest, CancelImmediatelyAfterSingleShot) {
  SafeTimer timer("TestCancelImmediatelyAfterSingleShot");
  EXPECT_CALL(callback_mock, CallbackMethod()).Times(0);
  int time_ms = 100;  // Delay time in milliseconds
  timer.SingleShot(time_ms, &CallbackMock::CallbackMethod, &callback_mock);
  timer.Cancel();
  // Sleep for an additional 100ms to ensure callback is not called
  std::this_thread::sleep_for(std::chrono::milliseconds(time_ms + 100));
}

TEST_F(SafeTimerTest, CancelAfterSingleShot) {
  SafeTimer timer("TestCancelAfterSingleShot");
  EXPECT_CALL(callback_mock, CallbackMethod()).Times(1);
  int time_ms = 100;  // Delay time in milliseconds
  auto ret = timer.SingleShot(time_ms, &CallbackMock::CallbackMethod, &callback_mock);
  EXPECT_TRUE(ret);
  // Sleep for an additional 100ms to ensure execution
  std::this_thread::sleep_for(std::chrono::milliseconds(time_ms + 100));
  timer.Cancel();
}

TEST_F(SafeTimerTest, Pause) {
  SafeTimer timer("Pause");
  int repeat_count = 2;  // Number of times repeat should execute
  int time_ms = 500;  // Delay time in milliseconds

  EXPECT_CALL(callback_mock, CallbackMethod()).Times(repeat_count - 1);
  // Execute once immediately
  timer.Repeat(time_ms, true, &CallbackMock::CallbackMethod, &callback_mock);
  // Sleep for 100ms less to ensure pause is called in time
  std::this_thread::sleep_for(std::chrono::milliseconds((repeat_count - 1) * time_ms - 100));
  auto ret = timer.Pause();
  EXPECT_TRUE(ret);
}

TEST_F(SafeTimerTest, Resume) {
  SafeTimer timer("Resume");
  int repeat_count = 3;  // Number of times repeat should execute
  int time_ms = 100;  // Delay time in milliseconds

  EXPECT_CALL(callback_mock, CallbackMethod()).Times(repeat_count);
  // Execute once immediately
  timer.Repeat(time_ms, true, &CallbackMock::CallbackMethod, &callback_mock);
  int time_advance_pause = 50;  // Time in milliseconds to pause in advance
  // Sleep for time_advance_pause ms less to ensure pause is called in time
  std::this_thread::sleep_for(std::chrono::milliseconds((repeat_count - 1) * time_ms - time_advance_pause));
  timer.Pause();
  timer.Resume();
  // Sleep for an additional 100ms to ensure timer execution is completed
  std::this_thread::sleep_for(std::chrono::milliseconds(time_advance_pause + 100));
}

int main(int argc, char** argv) {
  testing::InitGoogleMock(&argc, argv);
  return RUN_ALL_TESTS();
}
